#include "pilink_core.h"

void pilink_handler_init(struct pilink_handler_s *handler)
{
    handler->status = GSTATUS_IDLE;
}

int pilink_handler_parse(struct pilink_handler_s *handler,unsigned char ch)
{
    switch(handler->status){
        case GSTATUS_IDLE:
            if(ch == PILINK_HEAD1){
                handler->status = GSTATUS_HEAD;
            }
            break;
        case GSTATUS_HEAD:
            if(ch == PILINK_HEAD2){
                handler->status = GSTATUS_SENDER;
            }else{
                handler->status = GSTATUS_IDLE;
				return -1;
            }
            break;
        case GSTATUS_SENDER:
            handler->sender = ch;
            handler->status = GSTATUS_PORT1;
            break;
            
        case GSTATUS_PORT1:
            handler->type  =  ch;
            handler->type  = (handler->type << 8) & 0xFF00;
            handler->status = GSTATUS_PORT2;
            break;

        case GSTATUS_PORT2:
            handler->type |=  ch;;
            handler->status = GSTATUS_LEN1;
            break;
        case GSTATUS_LEN1:
            handler->length = ch;
            handler->length  = (handler->length << 8) & 0xFF00;
            
            handler->status = GSTATUS_LEN2;
            break;
        case GSTATUS_LEN2:
            handler->length |= ch;
            handler->status = GSTATUS_DATA;
            handler->wptr = 0;
            break;
        case GSTATUS_DATA:
            handler->buf[handler->wptr++] = ch;
            if(handler->wptr > handler->length - 1){
                handler->status = GSTATUS_CHECK;
                handler->buf[handler->wptr] = 0;
            }
			if(handler->wptr > (PILINK_HANDLER_BUFFER_LENGTH - 1)){
				handler->status = GSTATUS_IDLE;
				return -2;
			}
            break;
        case GSTATUS_CHECK:
            handler->checksum = ch;
            handler->status = GSTATUS_END1;
            break;
        case GSTATUS_END1:
            if(ch == PILINK_END1){
                handler->status = GSTATUS_END2;
            }else{     
                /* length is enough, but end byte 1 missed*/
                handler->status = GSTATUS_IDLE;
                return -3;
            }
            break;
        case GSTATUS_END2:
            if(ch == PILINK_END2){
                if(handler->checksum == pilink_handler_calc_checksum(handler)){
                    handler->status = GSTATUS_IDLE;
                    return 1;
                }else{
                    handler->status = GSTATUS_IDLE;
                    return -4;
                }
            }else{
                handler->status = GSTATUS_IDLE;
                return -5;
            }
            break;
        default:
            handler->status = GSTATUS_IDLE;
            break; 
    };
    return 0;
}

unsigned char pilink_handler_calc_checksum(struct pilink_handler_s *handler)
{
    int i;
    unsigned int sum;
    
    for(i = 0, sum = 0; i < handler->length; i++){
        sum += handler->buf[i];
    }
    
    return (unsigned char)(sum & 0xFF);
}

unsigned char* pilink_handler_get_data(struct pilink_handler_s *handler)
{
    return handler->buf;
}

int pilink_msg_pack(unsigned char *buffer,unsigned char sender,unsigned short type, unsigned char *dat, unsigned short len)
{ 
    unsigned int sum;
    int counter = 0;
    int i;
    
    buffer[counter++] = PILINK_HEAD1;
    buffer[counter++] = PILINK_HEAD2;
    buffer[counter++] = sender;
    buffer[counter++] = (unsigned char)(type >> 8);
    buffer[counter++] = (unsigned char)(type & 0x00FF);
    buffer[counter++] = (unsigned char)(len >> 8);
    buffer[counter++] = (unsigned char)(len & 0x00FF);
    
    for(i = 0,sum = 0; i < len; i++){
        buffer[counter++] = dat[i];
        sum += dat[i];
    }
    
    buffer[counter++] = (unsigned char)(sum & 0xFF);
    buffer[counter++] = PILINK_END1;
    buffer[counter++] = PILINK_END2;
    
    return counter;
}




int pilink_transceiver_init(struct pilink_transceiver_s *xceiver, int sender_id, struct pilink_channel_s *channel, pilink_transceiver_callback_t cb)
{
    if(xceiver == NULL){
        return -1;
    }

    if(channel == NULL){
        return -2;
    }

    xceiver->msg_cb    = cb;
    xceiver->channel   = channel;
    xceiver->sender_id = sender_id; 
    pilink_handler_init(&(xceiver->handler));
    if(channel->init != NULL){
        channel->init(channel);
    }

    return 0;
}

int pilink_transceiver_config(struct pilink_transceiver_s *xceiver, void *cfg)
{
    if(xceiver == NULL){
        return -1;
    }

    if(cfg == NULL){
        return -2;
    }

    if(xceiver->channel == NULL){
        return -3;
    }

    if(xceiver->channel->config == NULL){
        return -4;
    }

    xceiver->channel->config(xceiver->channel);

    return 0;
}

int pilink_transceiver_send(struct pilink_transceiver_s *xceiver, unsigned short type, unsigned char *dat, unsigned short len)
{
    if(xceiver == NULL){
        return -1;
    }

    if(xceiver->channel == NULL){
        return -2;
    }

    if(xceiver->channel->send == NULL){
        return -3;
    }

    int tx_len = pilink_msg_pack(xceiver->txbuf, xceiver->sender_id, type, dat, len);

    return xceiver->channel->send(xceiver->channel, xceiver->txbuf, tx_len);
}

int pilink_transceiver_run(struct pilink_transceiver_s *xceiver)
{
    if(xceiver == NULL){
        return -1;
    }

    if(xceiver->channel == NULL){
        return -2;
    }

    if(xceiver->channel->recv == NULL){
        return -2;
    }

    int recv_len = xceiver->channel->recv(xceiver->channel, xceiver->rxbuf, PILINK_TRANSCEIVER_BUFFER_LENGTH);

    if(recv_len > 0){
        for(int i = 0; i < recv_len; i++){
            if( pilink_handler_parse(&(xceiver->handler), xceiver->rxbuf[i]) > 0){
                if(xceiver->msg_cb != NULL){
                    xceiver->msg_cb(xceiver, xceiver->handler.type, xceiver->handler.buf, xceiver->handler.length);
                }
            }
        }
    }

    return 0;
}


int pilink_string_set(struct pilink_string_s *str, char *s)
{

    str->timestamp = 0x1234567887654321;
    str->len = strlen(s);
    if(str->len > (PILINK_HANDLER_BUFFER_LENGTH / 4)){
        str->len = (PILINK_HANDLER_BUFFER_LENGTH / 4);
    }
    strncpy(str->msg, s, str->len);

    return str->len;
}